package com.lipaluma.parser.builder;

import java.util.List;

import com.lipaluma.context.Context;
import com.lipaluma.exceptions.CsvParserException;
import com.lipaluma.field.adaptor.Adaptor;
import com.lipaluma.field.mapper.FieldAdapted;
import com.lipaluma.field.mapper.FieldAdaptedOnEachValue;
import com.lipaluma.field.mapper.FieldMapper;
import com.lipaluma.field.mapper.FieldValidated;
import com.lipaluma.field.mapper.FieldValidatedOnEachValue;
import com.lipaluma.field.mapper.builder.FieldMapperBuilder;
import com.lipaluma.field.mapper.builder.FieldMapperBuilder.Association;
import com.lipaluma.field.mapper.mapping.FieldMappingInfo;
import com.lipaluma.field.mapper.mapping.TargetMappingInfo;
import com.lipaluma.field.merge.FieldMergedAlgorithm;
import com.lipaluma.field.merge.TargetMergedAlgorithm;
import com.lipaluma.field.validator.Validator;
import com.lipaluma.parser.AbstractDataParser;
import com.lipaluma.parser.DataParser;

public class FromParserFieldMapperBuilder<BUILDER extends AbstractDataParser.Builder<? extends DataParser<TARGET>, TARGET>, TARGET> implements FromParserNeedMappingBuilder<BUILDER, TARGET>, FromParserAddNewFieldMapperBuilder<BUILDER, TARGET>, FromParserFieldMapperAfterRegisterBuilder<BUILDER, TARGET>, FromParserObjectFieldMapperAfterRegisterBuilder<BUILDER, TARGET> {

	protected BUILDER container;
	protected FieldMapperBuilder delegate;
	
	private FromParserFieldMapperBuilder(Class<TARGET> clazz, BUILDER container) {
		this.delegate = (FieldMapperBuilder)FieldMapperBuilder.forClass(clazz);
		this.container = container;
	}

	public static <BUILDER extends AbstractDataParser.Builder<? extends DataParser<TARGET>, TARGET>, TARGET> FromParserFieldMapperBuilder<BUILDER, TARGET> create(BUILDER container, Class<TARGET> targetClass) {
		return new FromParserFieldMapperBuilder<BUILDER, TARGET>(targetClass, container);
	}
	
	@Override
	public FromParserFieldMapperBuilder<BUILDER, TARGET> addFieldMapper(String fieldName, FieldMapper mapper) {
		delegate.addFieldMapper(fieldName, mapper);
		return this;
	}

	@Override
	public FromParserNeedMappingBuilder<BUILDER, TARGET> mapFieldAutomatically(String fieldName) {
		delegate.mapFieldAutomatically(fieldName);
		return this;
	}

	@Override
	public FromParserNeedMappingBuilder<BUILDER, TARGET> mapMultipleFieldAutomatically(String fieldName) {
		delegate.mapMultipleFieldAutomatically(fieldName);
		return this;
	}

	@Override
	public FromParserFieldMapperBuilder<BUILDER, TARGET> mapOn(FieldMappingInfo... mappings) {
		delegate.mapOn(mappings);
		return this;
	}

	@Override
	public FromParserInitMappingBuilder<BUILDER, TARGET> map() {
		Association association = delegate.getLastAssociation();
		return new FromParserInitMappingBuilder<BUILDER, TARGET>(this, association.getFieldMapper(), association.getField());
	}

	@Override
	public FromParserAddNewFieldMapperBuilder<BUILDER, TARGET> mapNextFieldInOneToMany() {
		delegate.mapNextFieldInOneToMany();
		return this;
	}
	
	@Override
	public FromParserFieldMapperBuilder<BUILDER, TARGET> mapObject(String fieldName, List<FieldMapper> fieldMappers) {
		this.delegate = (FieldMapperBuilder)delegate.mapObject(fieldName, fieldMappers);
		return this;
	}

	@Override
	public FromParserAddNewFieldMapperBuilder<BUILDER, TARGET> mapObject(String fieldName, Class<?> newTargetClass){
		this.delegate = (FieldMapperBuilder)delegate.mapObject(fieldName, newTargetClass);
		return this;
	}
	
	@Override
	public FromParserObjectFieldMapperAfterRegisterBuilder<BUILDER, TARGET> targetMergeable() {
		this.delegate = (FieldMapperBuilder)delegate.targetMergeable();
		return this;
	}
	
	@Override
	public FromParserObjectFieldMapperAfterRegisterBuilder<BUILDER, TARGET> mergeTargetWith(TargetMergedAlgorithm algorithm, String... params) {
		this.delegate = (FieldMapperBuilder)delegate.mergeTargetWith(algorithm, params);
		return this;
	}

	@Override
	public FromParserObjectFieldMapperAfterRegisterBuilder<BUILDER, TARGET> addTargetMappingInfo(TargetMappingInfo targetMappingInfo) {
		this.delegate = (FieldMapperBuilder)delegate.addTargetMappingInfo(targetMappingInfo);
		return this;
	}
	
	@Override
	public FromParserObjectFieldMapperAfterRegisterBuilder<BUILDER, TARGET> endMappingObject() {
		this.delegate = (FieldMapperBuilder)delegate.endMappingObject();
		return this;
	}

	@Override
	public FromParserFieldMapperAfterRegisterBuilder<BUILDER, TARGET> asId() {
		this.delegate = (FieldMapperBuilder)delegate.asId();
		return this;
	}

	@Override
	public FromParserFieldMapperAfterRegisterBuilder<BUILDER, TARGET> mergeWith(FieldMergedAlgorithm algorithm) {
		this.delegate = (FieldMapperBuilder)delegate.mergeWith(algorithm);
		return this;
	}

	@Override
	public FromParserFieldMapperAfterRegisterBuilder<BUILDER, TARGET> mergeWith(FieldMergedAlgorithm algorithm, String... params) {
		this.delegate = (FieldMapperBuilder)delegate.mergeWith(algorithm, params);
		return this;
	}

	@Override
	public FromParserValidatorBuilder<BUILDER, TARGET> validate() {
		FieldMapper fieldMapper = delegate.getLastAssociation().getFieldMapper();
		if(!FieldValidated.class.isAssignableFrom(fieldMapper.getClass()))
			throw new CsvParserException("Error on validation of field mapper '"+fieldMapper.getField().getName()+"' : The class "+fieldMapper.getClass()+" doesn't implements "+FieldValidated.class);
		return new FromParserValidatorBuilder<BUILDER, TARGET>(this, (FieldValidated)fieldMapper);
	}

	@Override
	public FromParserValidatorOnEachValueBuilder<BUILDER, TARGET> validateOnEachValue() {
		FieldMapper fieldMapper = delegate.getLastAssociation().getFieldMapper();
		if(!FieldValidatedOnEachValue.class.isAssignableFrom(fieldMapper.getClass()))
			throw new CsvParserException("Error on validation of field mapper '"+fieldMapper.getField().getName()+"' : The class "+fieldMapper.getClass()+" doesn't implements "+FieldValidatedOnEachValue.class);
		return new FromParserValidatorOnEachValueBuilder<BUILDER, TARGET>(this, (FieldValidatedOnEachValue)fieldMapper);
	}

	@Override
	public FromParserAdaptorBuilder<BUILDER, TARGET> adapt() {
		FieldMapper fieldMapper = delegate.getLastAssociation().getFieldMapper();
		if(!FieldAdapted.class.isAssignableFrom(fieldMapper.getClass()))
			throw new CsvParserException("Error on validation of field mapper '"+fieldMapper.getField().getName()+"' : The class "+fieldMapper.getClass()+" doesn't implements "+FieldAdapted.class);
		return new FromParserAdaptorBuilder<BUILDER, TARGET>(this, (FieldAdapted)fieldMapper);
	}

	@Override
	public FromParserAdaptorOnEachValueBuilder<BUILDER, TARGET> adaptOnEachValue() {
		FieldMapper fieldMapper = delegate.getLastAssociation().getFieldMapper();
		if(!FieldAdaptedOnEachValue.class.isAssignableFrom(fieldMapper.getClass()))
			throw new CsvParserException("Error on validation of field mapper '"+fieldMapper.getField().getName()+"' : The class "+fieldMapper.getClass()+" doesn't implements "+FieldAdaptedOnEachValue.class);
		return new FromParserAdaptorOnEachValueBuilder<BUILDER, TARGET>(this, (FieldAdaptedOnEachValue)fieldMapper);
	}

	@Override
	public FromParserFieldMapperAfterRegisterBuilder<BUILDER, TARGET> validate(Validator... validators) {
		delegate.validate(validators);
		return this;
	}

	@Override
	public FromParserFieldMapperAfterRegisterBuilder<BUILDER, TARGET> validateOnEachValue(Validator... validators) {
		delegate.validateOnEachValue(validators);
		return this;
	}

	@Override
	public FromParserFieldMapperAfterRegisterBuilder<BUILDER, TARGET> adapt(Adaptor... adaptors) {
		delegate.adapt(adaptors);
		return this;
	}

	@Override
	public FromParserFieldMapperAfterRegisterBuilder<BUILDER, TARGET> adaptOnEachValue(Adaptor... adaptors) {
		delegate.adaptOnEachValue(adaptors);
		return this;
	}

	@Override
	public BUILDER end() {
		container.withFieldMappers(delegate.build());
		return container;
	}

	public Context getContext() {
		return delegate.getContext();
	}
}
